﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.IO;
using Sirenix.Utilities;

namespace Jelly.Editor.AssetBundle
{
    /// <summary>
    /// 处理资源在包内还是包外，也就是DyanimcAsset
    /// </summary>
    internal static class DynamicAssetHelper
    {
        /// <summary>
        /// 生成只动态下载，不随包发布的资源列表
        /// </summary>
        /// <param name="assetMap"></param>
        public static void GenerateDynamicAssets(Dictionary<string, EditorAssetNode> assetMap,
            Dictionary<string, string> placeHolderMap)
        {
            bool webgl = false;
#if UNITY_WEBGL
            webgl = true;
#endif
            if (webgl || AssetBuilderCfg.Instance.useSyncLoadAssetList)
                MarkSyncLoadAssetNode(assetMap, placeHolderMap);
            else
                GenerateOutOfPackagesAssets(assetMap);
        }

        private static void GenerateOutOfPackagesAssets(Dictionary<string, EditorAssetNode> assetMap)
        {
            var sets = ReadPathListFromFile(AssetBuilderCfg.Instance.webDownloadCfgPath);
            if (sets == null)
                return;

            foreach (var path in sets)
            {
                if (assetMap.ContainsKey(path))
                {
                    assetMap[path].dynamic = true;
                }
            }

            string[] outOfPackageAssets = sets.Where(path => assetMap.ContainsKey(path))
                .Select(path => "\"" + assetMap[path].id + "\"").ToArray();
            string outOfPackageAssetsFileName = $"{Application.dataPath}/Scripts/OutOfPackageAssets.cs";
            WritePathListToScript(outOfPackageAssets, outOfPackageAssetsFileName);
        }

        private static HashSet<string> ReadPathListFromFile(string filepath)
        {
            if (!File.Exists(filepath))
            {
                return null;
            }

            string[] files = File.ReadAllLines(filepath);
            HashSet<string> sets = new HashSet<string>();
            foreach (var file in files)
            {
                if (file.StartsWith("#"))
                    continue;

                sets.Add(file);
            }

            return sets;
        }

        private static void WritePathListToScript(string[] pathList, string scriptPath,
            string replaceTemplate = "public static HashSet<string> list = new HashSet<string>()",
            string startBracket = "{", string endBracket = "}")
        {
            if (File.Exists(scriptPath))
            {
                string fileContent = File.ReadAllText(scriptPath);
                int templateIndex = fileContent.IndexOf(replaceTemplate);
                if (templateIndex >= 0)
                {
                    var firstBrace = fileContent.IndexOf(startBracket, templateIndex + replaceTemplate.Length);
                    if (firstBrace > 0)
                    {
                        var secondBrace = fileContent.IndexOf(endBracket, firstBrace + startBracket.Length);
                        if (secondBrace > 0)
                        {
                            fileContent = fileContent.Remove(firstBrace, secondBrace - firstBrace + endBracket.Length);
                        }
                    }
                }

                if (pathList.Length > 0)
                {
                    fileContent = fileContent.Replace(replaceTemplate,
                        replaceTemplate + $"{startBracket}\n" + string.Join(",\n", pathList) + $"\n{endBracket}");
                }

                File.WriteAllText(scriptPath, fileContent);
            }
        }

        /// <summary>
        /// 通过同步加载的资源列表，算出对应的文件列表，包含依赖文件
        /// </summary>
        /// <param name="assetMap"></param>
        private static void MarkSyncLoadAssetNode(Dictionary<string, EditorAssetNode> assetMap,
            Dictionary<string, string> placeHolderMap)
        {
            var syncLoadAssets = ReadPathListFromFile(AssetBuilderCfg.Instance.syncLoadAssetListPath);
            if (syncLoadAssets == null)
                return;

            // 全设置要下载，只有同步的设置为留包内
            foreach (var v in assetMap)
                v.Value.dynamic = true;
            HashSet<string> files = new HashSet<string>();
            foreach (var v in syncLoadAssets)
            {
                if (string.IsNullOrEmpty(v))
                    continue;
                if (assetMap.TryGetValue(v, out var node))
                {
                    AddAssetMapDependency(node, "列表指定", assetMap, files);
                }
                else if (placeHolderMap.TryGetValue(v, out var groupPath))
                {
                    // 占位符要到对应bundle里找，但不用递归
                    if (assetMap.TryGetValue(groupPath, out var groupNode))
                        AddAssetMapDependency(groupNode, v, assetMap, files, false);
                    else
                        Debug.LogError($"没找到placeholder归属的资源，要用原文件后缀: {v}, {groupNode}");
                }
                else
                {
                    Debug.LogError($"没找到同步资源，无法强制包内，要用原文件后缀: {v}");
                }
            }
            // 此时没有md5，不生成文件列表
        }

        private static void AddAssetMapDependency(EditorAssetNode node, string fromWhichNode,
            Dictionary<string, EditorAssetNode> assetMap, HashSet<string> files, bool recursive = true)
        {
            var nodePath = $"\"{node.id}\"";
            if (files.Contains(nodePath))
                return;

            node.dynamic = false;
            files.Add(nodePath);
            // Debug.Log($"file added to syncLoadFile: {node.id}, because: {fromWhichNode}");
            if (node.dependence != null && recursive)
            {
                foreach (var dep in node.dependence)
                {
                    AddAssetMapDependency(dep, node.id, assetMap, files);
                }
            }
        }

        /// <summary>
        /// 把下载或包内文件列表写到脚本中， assetMap的md5等信息被填充后，添加了placeHolder才能执行
        /// </summary>
        /// <param name="assetMap"></param>
        public static void TyrWriteFileListToScript(Dictionary<string, EditorAssetNode> assetMap)
        {
            bool webgl = false;
#if UNITY_WEBGL
            webgl = true;
#endif
            if (webgl || AssetBuilderCfg.Instance.useSyncLoadAssetList)
            {
                var syncLoadAssets = ReadPathListFromFile(AssetBuilderCfg.Instance.syncLoadAssetListPath);
                if (syncLoadAssets == null)
                    return;

                bool searchAssetConfig = false;

                HashSet<string> preloadFiles = new HashSet<string>();   // placeHolder有重复
                foreach (var v in assetMap)
                {
                    if (v.Value.dynamic == false)
                    {
                        string p = ResourceHelper.GetAssetURL("", v.Value.url, v.Value.md5, false);
                        preloadFiles.Add($"\"{p}\"");
                    }
                }

                var fileList = preloadFiles.ToList();
                fileList.Sort();

                WritePathListToScript(fileList.ToArray(), $"{Application.dataPath}/Scripts/SyncLoadAssets.cs");
                fileList.Add("'$PRELOAD_LIST'");
                // 用],能防止文件命中含有]的问题
                WritePathListToScript(fileList.ToArray(),
                    $"{Application.dataPath}/WX-WASM-SDK-V2/Runtime/wechat-default/game.js",
                    "preloadDataList: ", "[", "],"); 
            }
        }
    }
}